#coding:utf-8

import pandas as pd
import datetime
from collections import Counter
import math
import numpy as np
import json
from chinese_calendar import is_workday
from math import sin, cos, radians
import math
import requests

class Data_pre_processing:    
#数据清洗--------------------------------------------------------------------------------
    def washing(df, a, b):
        """
        数据清洗函数
        
        Args:
            df: pandas DataFrame，待清洗的数据
            a: int，控制空值处理方式
               10: 删除包含空值的行
               20: 删除全为空的列  
               12: 先删除包含空值的行，再删除包含空值的列
               21: 先删除包含空值的列，再删除包含空值的行
               其他: 删除包含空值的行和列
            b: int，控制是否去除零值
               0: 不去除零值
               其他: 去除零值（保留最后一列）
               
        Returns:
            pandas DataFrame: 清洗后的数据
        """
        # 参数验证
        if not isinstance(df, pd.DataFrame):
            raise ValueError("输入必须是pandas DataFrame")
            
        # 空值处理 - 使用字典映射，提高代码可读性
        na_strategy = {
            10: lambda x: x.dropna(),                           # 去空值，行
            20: lambda x: x.dropna(axis=1, how='all'),           # 去空值,列
            12: lambda x: x.dropna().dropna(axis=1, how='any'), # 先去行，再去列
            21: lambda x: x.dropna(axis=1, how='any').dropna()   # 先去列，再去行
        }
        
        # 应用空值处理策略
        if a in na_strategy:
            df = na_strategy[a](df)
        else:
            # 默认策略：删除包含空值的行和列
            df = df.dropna(how='any').dropna(axis=1, how='any')
        
        # 零值处理
        if b != 0:
            # 获取除最后一列外的所有列
            feature_columns = df.columns[:-1].tolist()
            
            # 只对特征列进行零值过滤，保持最后一列（目标变量）不变
            if feature_columns:
                # 使用布尔索引，比循环更高效
                mask = df[feature_columns] != 0
                # 如果某行的特征列全为0，则过滤掉该行
                valid_rows = mask.any(axis=1)
                df = df[valid_rows]
        
        # 安全删除id列（如果不存在不会报错）
        df = df.drop(columns='id', errors='ignore')
        
        return df

    #归一化（训练模型）---------------------------------------------------------------------------------
    def noramlization_training(data):
        """
        训练数据归一化（最小-最大归一化）
        
        Args:
            data: pandas DataFrame，待归一化的训练数据
            
        Returns:
            tuple: (归一化后的DataFrame, 各列范围列表, 各列最小值列表)
        """
        # 参数验证
        if not isinstance(data, pd.DataFrame):
            raise ValueError("输入必须是pandas DataFrame")
            
        df = data.copy()
        
        # 使用pandas的向量化操作，避免逐列循环
        # 计算每列的最小值和最大值
        min_values = df.min()
        max_values = df.max()
        
        # 计算范围（最大值-最小值）
        ranges = max_values - min_values
        
        # 处理范围为零的情况（避免除零错误）
        ranges = ranges.replace(0, 1)
        
        # 应用归一化公式：(x - min) / (max - min)
        normalized_df = (df - min_values) / ranges
        
        # 处理可能出现的无穷大和NaN值
        normalized_df = normalized_df.replace([np.inf, -np.inf], 0)
        normalized_df = normalized_df.fillna(0)
        
        # 如果出现NAN的情况，数值一律改为1（按原逻辑）
        normalized_df = normalized_df.fillna(1)
        
        return normalized_df, ranges.tolist(), min_values.tolist()
    
    #归一化（模型预测）---------------------------------------------------------------------------------
    def noramlization_predict(data,list_range,list_min):
        df=data.copy()
        list0=df.columns.tolist()
        for z in range(len(list0)):
            list0111=df[list0[z]].values.tolist()
            list0112=[]
            for z1 in list0111:
                try:
                    list0112.append((z1-list_min(z))/(list_range[z]))
                except:
                    list0112.append(0)
            df.loc[:, list0[z]] = list0112

        #如果出现NAN的情况，数值一律改为1
        df = df.fillna(1)

        return df

class relevance_analysis:
    """
    相关性分析类
    提供多种相关系数计算方法，用于分析数据特征之间的相关性
    """
    
    @staticmethod
    def pearson_means(df1):
        """
        计算皮尔逊积矩相关系数（使用corrwith方法）
        
        Args:
            df1: DataFrame，包含数值型数据
            
        Returns:
            Series: 各特征与目标变量（最后一列）的相关系数
        """
        # 获取所有列名
        column_names = df1.columns.values.tolist()
        
        # 计算各特征与目标变量（最后一列）的相关系数
        correlations = df1.corrwith(df1[column_names[-1]])
        
        return correlations

    @staticmethod
    def correlation_coefficient(df1, corr_method):
        """
        计算各列相关系数
        
        支持多种相关系数计算方法：
        - 'pearson': 皮尔逊相关系数（线性相关）
        - 'kendall': 肯德尔相关系数（秩相关）
        - 'spearman': 斯皮尔曼相关系数（秩相关）
        
        Args:
            df1: DataFrame，包含数值型数据
            corr_method: str，相关系数计算方法 ('pearson', 'kendall', 'spearman')
            
        Returns:
            Series: 各特征与目标变量（最后一列）的相关系数
        """
        # 参数验证
        valid_methods = ['pearson', 'kendall', 'spearman']
        if corr_method not in valid_methods:
            raise ValueError(f"不支持的相关系数方法: {corr_method}. "
                           f"支持的方法: {', '.join(valid_methods)}")
        
        # 获取所有列名
        column_names = df1.columns.values.tolist()
        target_column = column_names[-1]  # 目标变量（最后一列）
        
        try:
            # 计算相关系数矩阵，并提取与目标变量的相关系数
            correlation_matrix = df1.corr(method=corr_method)
            correlations = correlation_matrix[target_column]
            
            return correlations
            
        except Exception as e:
            raise RuntimeError(f"计算相关系数时出错: {str(e)}")

class data_time_handle:
    # 判断是否是工作日（支持自定义日期列名）-----------------------------------------
    def Is_Workday(df, date_column='date_date'):
        df_copy = df.copy()
        # 确保日期列是datetime类型
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        # 使用chinese_calendar判断是否是工作日
        df_copy['is_workday'] = df_copy[date_column].apply(lambda x: 1 if is_workday(x) else 0)
        return df_copy

    # 判断是否是周末（支持自定义日期列名）------------------------------------------
    def is_weekend(df, date_column='date_date'):
        df_copy = df.copy()
        # 确保日期列是datetime类型
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        # 0=Monday, 6=Sunday，5和6表示周末
        df_copy['is_weekend'] = df_copy[date_column].dt.weekday.apply(lambda x: 1 if x >= 5 else 0)
        return df_copy
    
    # 提取年月日信息------------------------------------------------------------------
    def extract_year_month_day(df, date_column='date_date'):
        df_copy = df.copy()
        # 确保日期列是datetime类型
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        df_copy['year'] = df_copy[date_column].dt.year
        df_copy['month'] = df_copy[date_column].dt.month
        df_copy['day'] = df_copy[date_column].dt.day
        return df_copy
    
    # 提取季度信息--------------------------------------------------------------------
    def extract_quarter(df, date_column='date_date'):
        df_copy = df.copy()
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        df_copy['quarter'] = df_copy[date_column].dt.quarter
        return df_copy
    
    # 提取一年中的第几天----------------------------------------------------------------
    def extract_day_of_year(df, date_column='date_date'):
        df_copy = df.copy()
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        df_copy['day_of_year'] = df_copy[date_column].dt.dayofyear
        return df_copy
    
    # 提取星期几（数字表示）----------------------------------------------------------
    def extract_weekday(df, date_column='date_date'):
        df_copy = df.copy()
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        df_copy['weekday_num'] = df_copy[date_column].dt.weekday  # 0=Monday, 6=Sunday
        return df_copy
    
    # 提取星期几（中文表示）----------------------------------------------------------
    def extract_weekday_chinese(df, date_column='date_date'):
        df_copy = df.copy()
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        weekday_map = {0: '周一', 1: '周二', 2: '周三', 3: '周四', 4: '周五', 5: '周六', 6: '周日'}
        df_copy['weekday_chinese'] = df_copy[date_column].dt.weekday.map(weekday_map)
        return df_copy
    
    # 提取是否是月初------------------------------------------------------------------
    def is_month_start(df, date_column='date_date'):
        df_copy = df.copy()
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        df_copy['is_month_start'] = df_copy[date_column].dt.is_month_start.astype(int)
        return df_copy
    
    # 提取是否是月末------------------------------------------------------------------
    def is_month_end(df, date_column='date_date'):
        df_copy = df.copy()
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        df_copy['is_month_end'] = df_copy[date_column].dt.is_month_end.astype(int)
        return df_copy
    
    # 提取季节信息--------------------------------------------------------------------
    def extract_season(df, date_column='date_date'):
        df_copy = df.copy()
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        # 定义季节：春(3-5), 夏(6-8), 秋(9-11), 冬(12-2)
        season_map = {1: 4, 2: 4, 3: 1, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 3, 10: 3, 11: 3, 12: 4}
        df_copy['season'] = df_copy[date_column].dt.month.map(season_map)
        
        # 添加中文季节
        season_chinese_map = {1: '春季', 2: '夏季', 3: '秋季', 4: '冬季'}
        df_copy['season_chinese'] = df_copy['season'].map(season_chinese_map)
        return df_copy
    
    # 提取月份中的第几周--------------------------------------------------------------
    def extract_week_of_month(df, date_column='date_date'):
        df_copy = df.copy()
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        # 计算一个月中的第几周
        df_copy['week_of_month'] = df_copy[date_column].apply(lambda x: (x.day - 1) // 7 + 1)
        return df_copy
    
    # 提取时间差特征（相对于某一日期）------------------------------------------------
    def extract_date_diff(df, date_column='date_date', reference_date=None):
        df_copy = df.copy()
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        # 如果没有提供参考日期，使用当前日期
        if reference_date is None:
            reference_date = datetime.datetime.now()
        elif isinstance(reference_date, str):
            reference_date = datetime.datetime.strptime(reference_date, "%Y-%m-%d")
        
        df_copy['days_from_reference'] = (df_copy[date_column] - reference_date).dt.days
        return df_copy
    
    # 综合提取所有日期特征------------------------------------------------------------
    def extract_all_date_features(df, date_column='date_date', reference_date=None):
        df_copy = df.copy()
        
        # 确保日期列是datetime类型
        if not pd.api.types.is_datetime64_any_dtype(df_copy[date_column]):
            df_copy[date_column] = pd.to_datetime(df_copy[date_column])
        
        # 提取年月日信息
        # df_copy['year'] = df_copy[date_column].dt.year
        # df_copy['month'] = df_copy[date_column].dt.month
        df_copy['day'] = df_copy[date_column].dt.day
        
        # # 提取季度信息
        # df_copy['quarter'] = df_copy[date_column].dt.quarter
        
        # 提取一年中的第几天
        df_copy['day_of_year'] = df_copy[date_column].dt.dayofyear
        
        # 提取星期几信息
        df_copy['weekday_num'] = df_copy[date_column].dt.weekday  # 0=Monday, 6=Sunday
        
        # 提取星期几的中文表示
        # weekday_map = {0: '周一', 1: '周二', 2: '周三', 3: '周四', 4: '周五', 5: '周六', 6: '周日'}
        # df_copy['weekday_chinese'] = df_copy[date_column].dt.weekday.map(weekday_map)
        
        # # 提取月初月末信息
        # df_copy['is_month_start'] = df_copy[date_column].dt.is_month_start.astype(int)
        # df_copy['is_month_end'] = df_copy[date_column].dt.is_month_end.astype(int)
        
        # 提取季节信息
        # season_map = {1: 4, 2: 4, 3: 1, 4: 1, 5: 1, 6: 2, 7: 2, 8: 2, 9: 3, 10: 3, 11: 3, 12: 4}
        # df_copy['season'] = df_copy[date_column].dt.month.map(season_map)
        # season_chinese_map = {1: '春季', 2: '夏季', 3: '秋季', 4: '冬季'}
        # df_copy['season_chinese'] = df_copy['season'].map(season_chinese_map)
        
        # 提取月份中的第几周
        df_copy['week_of_month'] = df_copy[date_column].apply(lambda x: (x.day - 1) // 7 + 1)
        
        # 提取时间差特征
        if reference_date is None:
            reference_date = datetime.datetime.now()
        elif isinstance(reference_date, str):
            reference_date = datetime.datetime.strptime(reference_date, "%Y-%m-%d")
        df_copy['days_from_reference'] = (df_copy[date_column] - reference_date).dt.days
        
        # 提取工作日信息
        df_copy['is_workday'] = df_copy[date_column].apply(lambda x: 1 if is_workday(x) else 0)
        
        # 提取周末信息
        df_copy['is_weekend'] = df_copy[date_column].dt.weekday.apply(lambda x: 1 if x >= 5 else 0)
        
        return df_copy
